"""
描述Swagger Open API 3.0格式的模式类型
"""
from functools import wraps
import os


__allow_type = ['str', 'int', 'float', 'bool', 'dict']


def _is_allow_type(value):
    """可被序列化的数据类型"""
    return type(value).__name__ in __allow_type


def _is_list_type(value):
    """数组类型"""
    return type(value).__name__ in ['list', 'tuple']


class SchemaBase:
    required_fields = list()  # 必填字段
    _all_fields = list()  # 全部字段

    def __init__(self, raw_json=None):
        self._object_fields = dict()
        self.raw_json = raw_json
        if raw_json is not None:
            self.from_dict(raw_json)

    def to_dict(self):
        rv = dict()
        for key in self.__class__.__dict__:
            # 如果实例属性是被property装饰，才会被序列化
            # 这是为了防止将实例的其他定义数据也被序列化出来，例如raw_json
            if key in self.__class__.__dict__ and isinstance(self.__class__.__dict__[key], property):
                rv[key] = self._value_to_dict(getattr(self, key))
        self.raw_json = rv
        return rv

    def from_dict(self, a_dict: dict):
        self.raw_json = a_dict
        for key, value in a_dict.items():
            setattr(self, key, self._dict_to_value(key, value))

    def _value_to_dict(self, value):
        """判断字段的类型"""
        if _is_allow_type(value):
            # 如果是可被序列化的类型，直接return
            return value
        if _is_list_type(value):
            # 如果是数组类型，需要递归判断数组内部每一个元素的类型
            rv = list()
            for item in value:
                rv.append(self._value_to_dict(item))
            return rv
        if isinstance(value, SchemaBase):
            # 如果字段类型是SchemaBase，则递归返回to_dict()的内容
            return value.to_dict()

    def _dict_to_value(self, key, value):
        if key in self._object_fields:
            rv = self._object_fields[key]()
            rv.from_dict(value)
            return rv
        if _is_allow_type(value):
            return value
        if _is_list_type(value):
            rv = list()
            for item in value:
                rv.append(self._dict_to_value(key, item))
            return rv